from typing import Optional, List, Literal

from typing_extensions import Self
from pydantic import BaseModel, model_validator


class ListMeetingsInput(BaseModel):
    """
    获取账户级的会议列表, 即将召开的、实时的、已结束的

        * 注意，通过会议号查询时，周期性会议会返回多条数据

    :raises: pydantic.ValidationError
    """

    # 会议状态。
    # 1：结束会议
    # 2：实时会议
    # 3：即将开始的会议
    meeting_state: int

    # 会议号，注意会议号不能包含 “-”
    meeting_code: Optional[str] = None

    # 搜索的开始时间（预定会议的开始时间），单位秒。
    start_time: Optional[int] = None

    # 搜索的结束时间（预定会议的开始时间），单位秒。
    end_time: Optional[int] = None

    # 操作者 ID。
    operator_id: Optional[str] = None

    # 操作者 ID 类型。
    # 1：userid
    operator_id_type: Optional[Literal[1]] = None

    # 分页查询页码，>= 1
    page: int = 1

    # 分页每页数据数量，1 <= page_size <= 20， 实测最大20，腾讯文档写的100000
    page_size: int = 20

    @model_validator(mode='after')
    def other_validate(self) -> Self:
        if self.meeting_state not in [1, 2, 3]:
            raise ValueError(
                f'腾讯会议查询api请求参数校验错误，参数“会议状态”的输入值“{self.meeting_state}”无效，可选值为 1、2、3。')

        if self.meeting_code and ('-' in self.meeting_code or ' ' in self.meeting_code):
            raise ValueError(
                f'腾讯会议查询api请求参数校验错误，参数“会议号”的输入值“{self.meeting_code}”无效，会议号不能包含“-”和空格。')

        if self.operator_id:
            if not self.operator_id_type or self.operator_id_type != 1:
                raise ValueError(
                    f'腾讯会议查询api请求参数校验错误，参数“操作者ID”和“操作者ID类型”必须一起配合使用，'
                    f'“操作者ID类型”的输入值“{self.operator_id_type}”无效，必须为1。')

        if self.page < 1:
            raise ValueError(f'腾讯会议查询api请求参数校验错误，参数“page”的输入值{self.page}无效，必须>=1')

        if not (1 <= self.page_size <= 20):
            raise ValueError(f'腾讯会议查询api请求参数校验错误，参数“page_size”的输入值{self.page_size}无效，必须1-20')

        if self.start_time is not None and (not isinstance(self.start_time, int) or self.start_time <= 0):
            raise ValueError(f'腾讯会议查询api请求参数校验错误，参数“start_time”的输入值{self.start_time}无效，必须是一个正整数')

        if self.end_time is not None and (not isinstance(self.end_time, int) or self.end_time <= 0):
            raise ValueError(f'腾讯会议查询api请求参数校验错误，参数“end_time”的输入值{self.end_time}无效，必须是一个正整数')

        return self


class DismissMeetingInput(BaseModel):
    """
    结束一个进行中的会议
    """
    # 会议的唯一 ID,
    meeting_id: str

    # 操作者 ID，根据 operator_id_type 的值，使用不同的类型。
    operator_id: Optional[str] = None

    # 操作者 ID 的类型：
    # 1：userid
    # 2：openid
    # 4：ms_open_id
    operator_id_type: Optional[Literal[1, 2, 4]] = None

    # 用户的终端设备类型：
    # 0：PSTN
    # 1：PC
    # 2：Mac
    # 3：Android
    # 4：iOS
    # 5：Web
    # 6：iPad
    # 7：Android Pad
    # 8：小程序
    # 9：voip、sip 设备
    # 10：Linux
    # 20：Rooms for Touch Windows
    # 21：Rooms for Touch MacOS
    # 22：Rooms for Touch Android
    # 30：Controller for Touch Windows
    # 32：Controller for Touch Android
    # 33：Controller for Touch iOS
    instanceid: Literal[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20, 21, 22, 30, 32, 33]

    # 原因代码，可为用户自定义。
    reason_code: int

    # 详细取消原因描述。
    reason_detail: Optional[str] = None

    # 是否强制结束会议，默认值为1：
    # 0：不强制结束会议，会议中有参会者，则无法强制结束会议
    # 1 ：强制结束会议，会议中有参会者，也会强制结束会议
    force_dismiss_meeting: Optional[Literal[0, 1]] = None

    # 是否回收会议号，默认值为0：
    # 0：不回收会议号，可以重新入会
    # 1： 回收会议号，不可重新入会
    # 说明：周期性会议如果还有子会议，需设置为不回收会议号，否则会导致后续子会议无法正常进行。
    # 此字段对快速会议不生效，快速会议会强制收回会议号。
    retrieve_code: Optional[Literal[0, 1]] = None

    @model_validator(mode='after')
    def other_validate(self) -> Self:
        if self.operator_id:
            if not self.operator_id_type or self.operator_id_type not in [1, 2, 4]:
                raise ValueError(
                    f'腾讯会议结束api请求参数校验错误，参数“操作者ID”和“操作者ID类型”必须一起配合使用，'
                    f'“操作者ID类型”的输入值{self.operator_id_type}无效，可选值为[1, 2, 4]。')

        return self


class GetMeetingDetailInput(BaseModel):
    """
    通过会议 ID 查询会议详情
    """

    # 有效的会议 ID。
    meeting_id: str

    # 操作者 ID。operator_id 必须与 operator_id_type 配合使用。根据 operator_id_type 的值，operator_id 代表不同类型。
    operator_id: Optional[str] = None

    # 操作者 ID 的类型：
    # 1：userid
    # 2：openid
    # 3：rooms_id
    operator_id_type: Optional[Literal[1, 2, 3]] = None

    # 用户的终端设备类型：
    # 0：PSTN
    # 1：PC
    # 2：Mac
    # 3：Android
    # 4：iOS
    # 5：Web
    # 6：iPad
    # 7：Android Pad
    # 8：小程序
    # 9：voip、sip 设备
    # 10：linux
    # 20：Rooms for Touch Windows
    # 21：Rooms for Touch MacOS
    # 22：Rooms for Touch Android
    # 30：Controller for Touch Windows
    # 32：Controller for Touch Android
    # 33：Controller for Touch iOS
    instanceid: Literal[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20, 21, 22, 30, 32, 33]

    @model_validator(mode='after')
    def other_validate(self) -> Self:
        if self.operator_id:
            if not self.operator_id_type or self.operator_id_type not in [1, 2, 3]:
                raise ValueError(
                    f'腾讯会议结束api请求参数校验错误，参数“操作者ID”和“操作者ID类型”必须一起配合使用，'
                    f'“操作者ID类型”的输入值{self.operator_id_type}无效，可选值为[1, 2, 3]。')

        return self


class GetMeetingDetailByCodeInput(BaseModel):
    """
    通过会议号 查询会议详情
    """

    # 有效的会议号，注意会议号不能包含 “-”
    meeting_code: str

    # 操作者 ID。operator_id 必须与 operator_id_type 配合使用。根据 operator_id_type 的值，operator_id 代表不同类型。
    operator_id: Optional[str] = None

    # 操作者 ID 的类型：
    # 1：userid
    # 2：openid
    # 3：rooms_id
    operator_id_type: Optional[Literal[1, 2, 3]] = None

    # 用户的终端设备类型：
    # 0：PSTN
    # 1：PC
    # 2：Mac
    # 3：Android
    # 4：iOS
    # 5：Web
    # 6：iPad
    # 7：Android Pad
    # 8：小程序
    # 9：voip、sip 设备
    # 10：linux
    # 20：Rooms for Touch Windows
    # 21：Rooms for Touch MacOS
    # 22：Rooms for Touch Android
    # 30：Controller for Touch Windows
    # 32：Controller for Touch Android
    # 33：Controller for Touch iOS
    instanceid: Literal[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20, 21, 22, 30, 32, 33]

    # 调用方用于标示用户的唯一 ID（企业内部请使用企业唯一用户标识；OAuth2.0 鉴权用户请使用 openId）。
    # 企业唯一用户标识说明：
    # 1：企业对接 SSO 时使用的员工唯一标识 ID。
    # 2：企业调用创建用户接口时传递的 userid 参数。
    userid: Optional[str] = None

    @model_validator(mode='after')
    def other_validate(self) -> Self:
        if '-' in self.meeting_code or ' ' in self.meeting_code:
            raise ValueError(
                f'腾讯会议查询api请求参数校验错误，参数“会议号”的输入值“{self.meeting_code}”无效，会议号不能包含“-”和空格。')

        if self.operator_id:
            if not self.operator_id_type or self.operator_id_type not in [1, 2, 3]:
                raise ValueError(
                    f'腾讯会议结束api请求参数校验错误，参数“操作者ID”和“操作者ID类型”必须一起配合使用，'
                    f'“操作者ID类型”的输入值{self.operator_id_type}无效，可选值为[1, 2, 3]。')

        return self


class GetMeetingParticipantsInput(BaseModel):
    """
    获取参会成员明细
    """

    # 会议的唯一 ID
    meeting_id: str

    # 周期性会议子会议 ID。
    # 说明：可通过查询用户的会议列表、查询会议接口获取返回的子会议 ID，即 current_sub_meeting_id；如果是周期性会议，此参数必传。
    sub_meeting_id: Optional[str] = None

    # 操作者 ID。operator_id 必须与 operator_id_type 配合使用。根据 operator_id_type 的值，operator_id 代表不同类型。
    operator_id: Optional[str] = None

    # 操作者 ID 的类型：
    # 3：rooms_id
    # 说明：当前仅支持 rooms_id。如操作者为企业内 userid 或 openId，请使用 userid 字段。
    operator_id_type: Optional[Literal[3]] = None

    # 会议创建者的用户 ID（企业内部请使用企业唯一用户标识；OAuth 2.0鉴权用户请使用 openId）。
    userid: Optional[str] = None

    # 分页获取参会成员列表的查询起始位置值。
    # 当参会成员较多时，建议使用此参数进行分页查询，避免查询超时。
    # 此参数为非必选参数，默认值为0，从头开始查询。 设置每页返回的数量，请参考参数“size”的说明。
    # 查询返回输出参数“has_remaining”为 true，表示该会议人数较多，还有一定数量的参会成员需要继续查询。
    # 返回参数“next_pos”的值即为下一次查询的 pos 的值。多次调用该查询接口直到输出参数“has_remaining”值为 false。
    pos: Optional[int] = None

    # 拉取参会成员条数，目前每页支持最大100条。
    size: Optional[int] = None

    # 筛选参会成员入会的起始时间（单位：秒）。
    start_time: Optional[int] = None

    # 筛选参会成员入会的结束时间（单位：秒）。
    end_time: Optional[int] = None

    @model_validator(mode='after')
    def other_validate(self) -> Self:
        if not self.operator_id and not self.userid:
            raise ValueError(
                f'获取参会成员明细api请求参数校验错误，参数“userid”和“operator_id”必须使用一个。')

        if self.operator_id:
            if not self.operator_id_type or self.operator_id_type not in [3]:
                raise ValueError(
                    f'获取参会成员明细api请求参数校验错误，参数“操作者ID”和“操作者ID类型”必须一起配合使用，'
                    f'“操作者ID类型”的输入值{self.operator_id_type}无效，可选值为[3]。')

        return self
